import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from logzero import logger
# from Common.upload_file import upload

from string import Template

# Template.substitute(A=)
class BasePage:
    """
    BasePage类，针对PageObjects类的二次封装
    """
    def __init__(self, d: WebDriver):
        self.driver = d

    def open(self, url):
        """
        打开网页
        :param url:
        :return:
        """
        self.driver.get(url)
        self.driver.implicitly_wait(10)

    def wait_element_to_be_visible(self,
                                   loc: tuple,
                                   timeout=20,
                                   frequency=0.5):
        """
        等待元素可见
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:

            logger.info("开始等待页面元素<{}>是否可见！".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(
                EC.visibility_of_element_located(loc))
        except Exception as e:
            logger.error("页面元素<{}>等待可见失败！".format(loc))
            raise e
        else:
            end_time = time.time()
            logger.info("页面元素<{}>等待可见，等待时间：{}秒".format(
                loc, round(end_time - start_time, 2)))

    def wait_element_to_be_click(self,
                                 loc: tuple,
                                 timeout=20,
                                 frequency=0.5):
        """
        等待元素可点击
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("开始等待页面元素<{}>是否可点击！".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout,
                          frequency).until(EC.element_to_be_clickable(loc))
        except Exception as e:
            logger.error("页面元素<{}>等待可点击失败！".format(loc))
            raise e
        else:
            end_time = time.time()
            logger.info("页面元素<{}>等待可点击，等待时间：{}秒".format(
                loc, round(end_time - start_time, 2)))

    def wait_element_to_be_exist(self,
                                 loc: tuple,
                                 timeout=20,
                                 frequency=0.5):
        """
        等待元素存在
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("开始等待页面元素<{}>是否存在！".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout,
                          frequency).until(EC.presence_of_element_located(loc))
        except Exception as e:
            logger.error("页面元素<{}>等待存在失败！".format(loc))
            raise e
        else:
            end_time = time.time()
            logger.info("页面元素<{}>等待存在，等待时间：{}秒".format(
                loc, round(end_time - start_time, 2)))

    def get_element(self, loc: tuple = None, find_way: str = None, value: str = None):
        """
        获取页面中的元素
        :param loc: 元素定位的XPATH元组表达式
        :param find_way: 元素定位的类型
        :param value: 元素定位的表达式
        :return: WebElement对象
        """
        logger.info("查找元素<{}>".format(loc))
        try:
            ele = self.driver.find_element(loc)
        except Exception as e:

            logger.error("查找元素<{}>失败！".format(loc))
            raise e
        else:
            return ele

    def get_elements(self, loc: tuple):
        """
        获取页面中的所有元素
        :param loc: 元素定位的XPATH元组表达式
        :return: WebElement对象
        """
        logger.info("查找所有元素<{}>".format(loc))
        try:
            ele = self.driver.find_elements(*loc)
        except Exception as e:
            logger.error("查找所有元素<{}>失败！".format(loc))
            raise e
        else:
            return ele

    def input_text(self, text, loc: tuple, timeout=20, frequency=0.5):
        """
        对输入框输入文本内容
        :param text: 输入的文本内容
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("输入元素<{}>的内容为{}".format(loc, text))
            self.wait_element_to_be_visible(loc, timeout, frequency)
            self.get_element(loc).send_keys(text)
        except Exception as e:
            logger.error("在元素<{}>中输入内容{}失败！".format(loc, text))
            raise e

    def clear_text(self, loc: tuple, timeout=20, frequency=0.5):
        """
        清除文本框的内容
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("清除元素<{}>的文本内容".format(loc))
            self.wait_element_to_be_click(loc, timeout, frequency)
            self.get_element(loc).clear()
        except Exception as e:
            logger.error("清除元素<{}>的文本内容失败！".format(loc))
            raise e

    def click_button(self, loc: tuple, timeout=20, frequency=0.5):
        """
        点击按钮
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("点击元素<{}>".format(loc))
            self.wait_element_to_be_click(loc, timeout, frequency)
            self.get_element(loc).click()
        except Exception as e:
            logger.error("点击元素<{}>失败！".format(loc))
            raise e

    def get_element_text(self, loc: tuple, timeout=20, frequency=0.5):
        """
        获取WebElement对象的文本值
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return: WebElement对象的文本值
        """
        try:
            logger.info("获取元素<{}>的文本值".format(loc))
            self.wait_element_to_be_visible(loc, timeout, frequency)
            text = self.get_element(loc).text
        except Exception as e:
            logger.error("获取元素<{}>的文本值失败！".format(loc))
            raise e
        else:
            logger.info("获取到的元素文本值为：{}".format(text))
            return text

    def get_elements_text(self, loc: tuple, timeout=20, frequency=0.5):
        """
        获取WebElement对象的所有文本值
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return: WebElement对象的文本值的列表
        """
        try:
            logger.info("获取元素<{}>的所有文本值".format(loc))
            self.wait_element_to_be_visible(loc, timeout, frequency)
            all_text = self.get_elements(loc)
            text_list = []
            for one_text in all_text:
                text_list.append(one_text.text)
        except Exception as e:
            logger.error("获取元素<{}>的所有文本值失败！".format(loc))
            raise e
        else:
            logger.info("获取到的元素文本值列表为：{}".format(text_list))
            return text_list

    def get_element_attr(self,
                         attr_name,
                         loc: tuple,
                         timeout=20,
                         frequency=0.5):
        """
        获取WebElement对象的属性值
        :param attr_name: 属性名称
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return: WebElement对象的属性值
        """
        try:
            logger.info("获取元素<{}>的属性{}的值".format(loc, attr_name))
            self.wait_element_to_be_exist(loc, timeout, frequency)
            value = self.get_element(loc).get_attribute(attr_name)
        except Exception as e:
            logger.error("获取元素<{}>的属性{}的值失败！".format(
                loc, attr_name))
            raise e
        else:
            logger.info("获取到的元素属性{}的值为{}".format(attr_name, value))
            return value

    def switch_to_frame(self, loc: tuple, timeout=20, frequency=0.5):
        """
        切换iframe页面
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("根据元素<{}>进行iframe切换".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(
                EC.frame_to_be_available_and_switch_to_it(loc))
        except Exception as e:
            logger.error("根据元素<{}>进行iframe切换失败！".format(loc))
            raise e
        else:
            end_time = time.time()
            logger.info("根据元素<{}>进行iframe切换，等待时间：{}秒".format(
                loc, round(end_time - start_time, 2)))

    def switch_to_default_content(self):
        """
        切换iframe到main页面
        :return:
        """
        try:
            logger.info("切换iframe到main页面")
            self.driver.switch_to.default_content()
        except Exception as e:
            logger.error("切换iframe到main页面失败！")
            raise e

    def switch_to_parent(self):
        """
        切换iframe到上一层页面
        :return:
        """
        try:
            logger.info("切换iframe到上一层页面")
            self.driver.switch_to.parent_frame()
        except Exception as e:
            logger.error("切换iframe到上一层页面失败！")
            raise e

    def upload_file(self, filename, browser_type="chrome"):
        """
        非input标签的文件上传
        :param filename: 文件名（绝对路径）
        :param browser_type: 浏览器类型
        :return:
        """
        try:
            logger.info("上传文件（{}）".format(filename))
            time.sleep(2)
            # upload(filePath=filename, browser_type=browser_type)
        except Exception as e:
            logger.error("上传文件（{}）失败！".format(filename))
            raise e
        else:
            time.sleep(2)

    def suspend_mouse(self, loc, timeout=20, frequency=0.5):
        """
        鼠标悬浮
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("根据元素<{}>进行悬浮".format(loc))
            self.wait_element_to_be_click(loc, timeout, frequency)
            ele = self.get_element(loc)
            ActionChains(self.driver).move_to_element(ele).perform()
        except Exception as e:
            logger.error("根据元素<{}>进行悬浮失败！".format(loc))
            raise e

    def alert_close(self,
                    alert_type='alert',
                    text=None,
                    timeout=20,
                    frequency=0.5):
        """
        弹框关闭
        :param alert_type: 弹框类型：alert/confirm/prompt
        :param text: prompt弹框输入的文本
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("切换并关闭{}类型的弹框".format(alert_type))
            start_time = time.time()
            WebDriverWait(self.driver, timeout,
                          frequency).until(EC.alert_is_present())
            if alert_type in ['alert', 'confirm']:
                self.driver.switch_to.alert.accept()
            elif alert_type == 'prompt':
                self.driver.switch_to.alert.send_keys(text)
                self.driver.switch_to.alert.accept()
            else:
                logger.error("不支持{},请确认alert的类型".format(alert_type))
        except Exception as e:
            logger.error("切换并关闭{}类型的弹框失败！".format(alert_type))
            raise e
        else:
            end_time = time.time()
            logger.info("切换并关闭{}类型的弹框，等待时间：{}秒".format(
                alert_type, round(end_time - start_time, 2)))

    def select_action(self,
                      loc,
                      content,
                      select_type,
                      timeout=20,
                      frequency=0.5):
        """
        Select操作
        :param loc: 元素定位的XPATH元组表达式
        :param content: select_by_方法的入参
        :param select_type: select类型
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("根据元素<{}>以{}方式进行下拉选择".format(
                loc, select_type))
            self.wait_element_to_be_click(loc, timeout, frequency)
            ele = self.get_element(loc)
            if select_type == 'index':
                Select(ele).select_by_index(content)
            elif select_type == 'value':
                Select(ele).select_by_value(content)
            elif select_type == 'text':
                Select(ele).select_by_visible_text(content)
            else:
                logger.error("不支持{},请确认Select的类型".format(select_type))
        except Exception as e:
            logger.error("根据元素<{}>以{}方式进行下拉选择失败！".format(
                loc, select_type))
            raise e

    def switch_to_windows(self, loc, timeout=20, frequency=0.5):
        """
        窗口切换
        :param loc: 元素定位的XPATH元组表达式
        :param timeout: 等待的超时时间
        :param frequency: 轮询频率
        :return:
        """
        try:
            logger.info("根据元素<{}>进行窗口切换".format(loc))
            cur_handles = self.driver.window_handles  # 获取点击之前的窗口总数
            start_time = time.time()
            self.click_button(loc, timeout, frequency)  # 点击按钮后新的窗口出现
            WebDriverWait(self.driver, timeout, frequency).until(
                EC.new_window_is_opened(cur_handles))
            wins = self.driver.window_handles  # 再次获取窗口总数
            self.driver.switch_to.window(wins[-1])  # 切换到新的页面
        except Exception as e:
            logger.error("根据元素<{}>进行窗口切换失败！".format(loc))
            raise e
        else:
            end_time = time.time()
            logger.info("根据元素<{}>进行窗口切换，等待时间：{}秒".format(
                loc, round(end_time - start_time, 2)))
